package com.atguigu.com.gmall.index.aspect;

import com.alibaba.fastjson.JSON;
import com.atguigu.com.gmall.index.annotation.GmallCache;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class GmallCachAop {
    @Autowired
    RBloomFilter bloomFilter;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    RedissonClient redissonClient;

    @Around("@annotation(com.atguigu.com.gmall.index.annotation.GmallCache)")
    public Object aroud(ProceedingJoinPoint point) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) point.getSignature();
        //获取方法对象
        Method method = signature.getMethod();
        //获取方法上的注解
        GmallCache annotation = method.getAnnotation(GmallCache.class);
        //获取注解中的前缀
        String prefix = annotation.prefix();
        String join = StringUtils.join(point.getArgs(), ",");
        String key = prefix+join;
        //使用布隆过滤器 防止缓存穿透
        if (!bloomFilter.contains(key)){
            return null;
        }
        //从缓存中查询数据
        String s = redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(s)){
            return JSON.parseObject(s,signature.getReturnType());
        }
        //如果没有 则查询数据库 加锁
        String lock = annotation.lock();
        RLock lock1 = redissonClient.getLock(lock + join);
        lock1.lock();
        try {
            //在次查询缓存中的数据
            String json = redisTemplate.opsForValue().get(key);
            if (StringUtils.isNotBlank(json)){
                return JSON.parseObject(json,signature.getReturnType());
            }
            //查询数据库
            //执行目标方法
            Object result = point.proceed(point.getArgs());
            //放入到缓存中 设置随机的过期时间
            int timeout = annotation.timeout()+ new Random().nextInt(annotation.random());
            String jsonString = JSON.toJSONString(result);
            redisTemplate.opsForValue().set(key,jsonString, timeout,TimeUnit.MINUTES);
            return result;
        } finally {
            lock1.unlock();
        }

    }
}
